mod file_processor;

use std::{
    collections::HashMap,
    hash::Hash,
    path::{Path, PathBuf},
};

pub use file_processor::*;
use libp2p::kad::Record;
use serde::{Deserialize, Serialize};

const CHUNK_FILE_EXTENSION: &str = "chunk";
const PROCESSING_RESULT_FILE_NAME: &str = "files.cbor";

pub fn make_chunks_directory_name(containing_dir: &Path, file_name: &str) -> PathBuf {
    containing_dir.join(format!("{}_chunks", file_name.replace(".", "_")))
}

pub fn make_chunk_file_name(index: usize) -> String {
    format!("{}.{}", index, CHUNK_FILE_EXTENSION)
}

pub fn make_file_metadata_path_name(chunks_directory: &Path) -> PathBuf {
    chunks_directory.join(PROCESSING_RESULT_FILE_NAME)
}

// PublishedFile 结构体是为了将FileProcessResult更小的发布到DHT(如果文件太大，那么hash的信息就会很大，导致写不进去DHT)
// 所以这里只抽取关键的信息即可
#[derive(Debug, Serialize, Deserialize)]
pub struct PublishedFile {
    pub number_of_chunks: usize,
    pub merkle_root: [u8; 32],
}

impl PublishedFile {
    pub fn new(number_of_chunks: usize, merkle_root: [u8; 32]) -> Self {
        Self {
            number_of_chunks,
            merkle_root,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct PublishedFileChunk {
    pub chunk_id: usize,
}

impl PublishedFileChunk {
    pub fn new(chunk_id: usize) -> Self {
        Self { chunk_id }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileProcessResult {
    pub original_file_name: String,
    pub number_of_chunks: usize,
    pub chunks_directory: PathBuf,
    pub merkle_root: [u8; 32],
    pub merkle_proofs: HashMap<usize, Vec<u8>>,
    pub public: bool,
}

impl From<&FileProcessResult> for PublishedFile {
    fn from(value: &FileProcessResult) -> Self {
        Self {
            number_of_chunks: value.number_of_chunks,
            merkle_root: value.merkle_root,
        }
    }
}

impl TryFrom<Vec<u8>> for FileProcessResult {
    type Error = serde_cbor::Error;

    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
        serde_cbor::from_slice(value.as_slice())
    }
}

impl Hash for FileProcessResult {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.original_file_name.hash(state);
        self.number_of_chunks.hash(state);
        self.chunks_directory.hash(state);
        self.merkle_root.hash(state);
        self.public.hash(state);
    }
}

impl TryFrom<&FileProcessResult> for Record {
    type Error = serde_cbor::Error;

    fn try_from(file_process_result: &FileProcessResult) -> Result<Self, Self::Error> {
        let (key, value) = file_process_result.key_value()?;
        Ok(Record::new(key, value))
    }
}

impl TryFrom<Vec<u8>> for PublishedFileChunk {
    type Error = serde_cbor::Error;

    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
        serde_cbor::from_slice(value.as_slice())
    }
}
